package cn.maidouya.hrm.config;

import cn.maidouya.hrm.handler.MyAccessDeniedHandler;
import cn.maidouya.hrm.handler.MyAuthenticationFailureHandler;
import cn.maidouya.hrm.handler.MyAuthenticationSuccessHandler;
import cn.maidouya.hrm.mapper.PermissionMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;

import javax.sql.DataSource;

/**
 *   1. controller回调接口
 *   2. 配置启动类
 *   3. 配置文件交给spring管理 @configuration 开启security支持启用@security
 *   4. UserDetailService使用springsecurity提供user类
 *   5. 密码解密器
 *      明文调用NOPassword
 *      解密调动解密方法解密
 *   6. 配置认证
 *      http调用授权
 *      要放行页面，登录页面必须放行，设置放行权限
 *      认证
 *      通过formLogin表单认证用户信息
 *      登录成功 回调controller接口
 *      登录退出
 */
@Configuration // 配置文件交给spring管理并启动【配置文件
@EnableWebSecurity // 开启security启动支持
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MyWebSecurityConfigurer extends WebSecurityConfigurerAdapter {
    @Autowired
    private PermissionMapper permissionMapper; // 通过注解自动获取方法
    // 创建UserDetailService的Bean，该组件是用来加载用户认证信息  模拟数据
   /* @Bean
    public UserDetailsService userDetailsService(){
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
        ArrayList<SimpleGrantedAuthority> auth = new ArrayList<>();
        User user = new User("xiaohong", "123", auth);
        manager.createUser(user);
        return manager;
    }*/
    // 配置编码器
    @Bean
    public PasswordEncoder geEncoder(){
        // 明文不需要解密
        //return NoOpPasswordEncoder.getInstance();
        // BCryptPasswordEncoder会对密文进行解密
        return new BCryptPasswordEncoder();
    }
    // 自己封装授权方式一 安全配置
    /*@Override
    protected void configure(HttpSecurity http) throws Exception {
        // 查询系统所有资源权限交给springsecurity管理
        List<Permission> authors =permissionMapper.loadAllPermission();

        ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry expressionInterceptUrlRegistry =
                http.authorizeRequests();
        // 遍历所有资源权限，每个资源权限交给springsecurity管理  hasAnyAuthority这个用户有哪些权限
        authors.stream().forEach(permission -> {
            expressionInterceptUrlRegistry.antMatchers(permission.getResource()).hasAnyAuthority(permission.getSn());
        });

        // http.authorizeRequests() //所有请求都要权限
        expressionInterceptUrlRegistry.antMatchers("/login").permitAll() // 对登录放行
                .antMatchers("/login.html").permitAll() // 对自定义登录页面放行
                .anyRequest().authenticated() // 其他请求需要认证访问
                .and().formLogin() // 允许表单登录
                // 回调地址
                //   .successForwardUrl("/success")
                // 登录成功回调给对象
                .successHandler(new MyAuthenticationSuccessHandler())
                // 登录失败回调
                .failureHandler(new MyAuthenticationFailureHandler())
                .loginPage("/login.html")   // 登录页面跳转地址
                .loginProcessingUrl("/login")   // 登录处理地址(必须)
                .and().logout().permitAll() // 登出放行
                .and().csrf().disable(); // 关闭跨站访问
    }*/

    // 注入数据库连接池对象
    @Autowired
    private DataSource dataSource;
    // 配置保存记住我token的repository  执行数据库数据
    @Bean
    public PersistentTokenRepository createTokenRepository(){
        // 创建jdbcrepository对象调用方法执行DataSource   返回token
        JdbcTokenRepositoryImpl repository = new JdbcTokenRepositoryImpl();
        repository.setDataSource(dataSource);
        // 自动创建表，这个数据表没有存在会自动创建，已经存在有这个数据表启动会报错
        //repository.setCreateTableOnStartup(true);
        return repository;
    }
    // httpsecurity安全配置
    @Autowired
    private UserDetailsService userDetailsService;
    // 授权规则配置
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 全注解授权方式二
        http.authorizeRequests()
                .antMatchers("/login").permitAll() // 对登录路径放行
                .antMatchers("/login.html").permitAll() // 对自定义登录页面放行
                .anyRequest().authenticated() // 其他请求需要认证访问
                .and().formLogin() // 允许表单方式登录
                // 回调地址
             //   .successForwardUrl("/success")
                // 登录认证成功成功回调
                .successHandler(new MyAuthenticationSuccessHandler())
                // 登录认证失败回调
                .failureHandler(new MyAuthenticationFailureHandler())
                .loginPage("/login.html")   // 自定义登录页面跳转地址，如没有自定义会使用security自带登录页面
                .loginProcessingUrl("/login")   // 自定义登录处理地址(必须)
                .and().logout().permitAll() // 退出登录放行
                .and().csrf().disable(); // 关闭跨站访问
        // 单独配置授权失败回调
        http.exceptionHandling().accessDeniedHandler(new MyAccessDeniedHandler());
        // 单独配置记住我    调用token中createTokenRepository方法 设置过期时间为3600(java时间)  是哪个用户userDetailsService记住我
        http.rememberMe().tokenRepository(createTokenRepository()).tokenValiditySeconds(3600).userDetailsService(userDetailsService);
    }
}